﻿
namespace BJYJ_Admin.Application.System.Services.Auth
{
    public class SysAuthService : IDynamicApiController, ITransient
    {
        private readonly ISqlSugarClient _db;
        private readonly IMemoryCache _memoryCache;
        private readonly ICaptcha _captcha;
        public SysAuthService(ISqlSugarClient db, IMemoryCache cache, ICaptcha captcha)
        {
            _db = db;
            _memoryCache = cache;
            _captcha = captcha;
        }

        /// <summary>
        /// 获取验证码，不进行鉴权验证
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        //不对返回结果进行监控
        [SuppressMonitor]
        [DisplayName("获取验证码")]
        //不对返回结果进行加密
        [NoEncrypt]
        public dynamic GetCaptcha()
        {
            var codeId = Guid.NewGuid().ToString().Replace("-", "");
            var captcha = _captcha.Generate(codeId);
            var expirySeconds = App.GetOptions<CaptchaOptions>()?.ExpirySeconds ?? 60;
            return new { Id = codeId, Img = captcha.Base64, ExpirySeconds = expirySeconds };
        }
        
        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        ///登陆接口，无需鉴权验证
        [AllowAnonymous]
        [SuppressMonitor]
        public async Task<UserLoginOutput> Login(UserLoginDto input)
        {
            // 校验验证码
            if (!_captcha.Validate(input.CodeId.ToString(), input.Code))
                throw Oops.Oh(ErrorCodeEnum.U0001);
            _captcha.Generate(input.CodeId.ToString());

            // 判断密码错误次数（缓存30分钟）
            var keyPasswordErrorTimes = $"sys_password_error_times:{input.Account}";

            var passwordErrorTimes = _memoryCache.Get<int>(keyPasswordErrorTimes);
            if (passwordErrorTimes > 4)
            {
                //处于锁定状态
                throw Oops.Oh(ErrorCodeEnum.U0004);
            }
            input.Account = SM4Encrypt.Decrypt_ECB(input.Account);
            var user = await _db.Queryable<SYS_USERS>().Where(x => x.USERNAME == input.Account).FirstAsync();
            if (user == null)
            {
                throw Oops.Oh(ErrorCodeEnum.U0002);
            }
            if (user.ISENABLED == 1)
            {
                throw Oops.Oh(ErrorCodeEnum.U0007);
            }
            if (user.PASSWD != MD5Encryption.Encrypt(user.USERNAME + SM4Encrypt.Decrypt_ECB(input.Password)))
            {
                // 密码错误次数+1
                _memoryCache.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
                if (passwordErrorTimes == 5)
                {
                    throw Oops.Oh("您已连续5次密码输入错误，账号已被锁定，请30分钟后再试！");
                }
                else
                {
                    throw Oops.Oh($"第{passwordErrorTimes}次密码输入错误，连续5次之后，账号将锁定30分钟！");
                }
            }
            var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>()
            {
                { "ID", user.ID },  // 存储Id
                { "USERNAME",user.USERNAME }, // 存储用户名
                { "TRUENAME",user.TRUENAME },//真实姓名
                { "AREANUMBER",user.AREANUMBER },//区划代码
                { "PHONE",user.PHONE },//电话
                { "EMAILE",user.EMAILE },//邮箱
                { "LASTLOGINDATE",user.LASTLOGINDATE},//最后一次登陆时间
                { "IMGURL",user.IMGURL },//头像
                { "JGJB",user.JGJB },//监管级别
                { "FKID",user.FKID},//外键ID
                { "ISENABLED",user.ISENABLED}//是否禁用
            });
            // 登录成功则清空密码错误次数
            _memoryCache.Remove(keyPasswordErrorTimes);
            //更新最后登录时间
            _db.Updateable<SYS_USERS>()
                              .SetColumns(it => it.LASTLOGINDATE == DateTime.Now)
                              .SetColumns(it => it.UPDATE_TIME == DateTime.Now)
                              .SetColumns(it => it.UPDATE_BY == user.USERNAME)
                              .Where(it => it.ID == user.ID)
                              .ExecuteCommand();
            //获取配置文件的token有效期
            int time = Convert.ToInt32(App.Configuration["JWTSettings:ExpiredTime"]);
            //获取刷新token的有效期，用于刷新token
            int RefTime = Convert.ToInt32(App.Configuration["JWTSettings:RefTime"]);
            return new UserLoginOutput
            {
                RefreshToken = JWTEncryption.GenerateRefreshToken(accessToken, RefTime),
                AccessToken = accessToken,
                ExpiredTime = time.ToString()
            };
        }

    }
}
